使用 .NET 產生帶有浮水印的 Excel
TLDR
- Excel 無內建浮水印功能,可透過設定「滿版透明背景圖片」模擬。
- 浮水印在「標準模式」與「整頁模式」可透過 Background 設定;「整頁模式」與「列印」則透過 Header 設定。
- 產生浮水印圖片時,需根據 Excel 的
PaperSize調整圖片尺寸,橫向紙張需將寬高對調。 - EPPlus 6 之後因移除
System.Drawing.Common依賴,相關實作方式可能失效。 - NPOI 可透過底層 API 操作 XML 結構(VML Drawing)來達成浮水印效果。
模擬浮水印的策略
Excel 本身並無浮水印功能,實務上可透過設定滿版背景圖片來達成。根據檢視模式的不同,設定方式如下:
- 標準模式與整頁模式:在 Background 設定圖片。
- 整頁模式與列印:在 Header 設定圖片。
- 分頁模式:目前尚無有效方法。
產生滿版浮水印圖片
由於 Excel 的版面設定(頁面方向與大小)決定了背景圖片的尺寸,開發者需根據 PaperSize 調整圖片大小。
取得紙張大小
可透過 System.Drawing.Printing.PrinterSettings 取得各類紙張的 RawKind 與對應尺寸。
csharp
PrinterSettings settings = new PrinterSettings() {
PrinterName = "Microsoft XPS Document Writer"
};
foreach (System.Drawing.Printing.PaperSize printerPaperSize in settings.PaperSizes) {
// 取得紙張名稱與寬高
}調整圖片尺寸
若圖片尺寸不符合頁面大小,需進行縮放或補白處理。
csharp
public Image ResizeImageBackgroundToFullPage(Image watermark, int width, int height){
if (watermark.Width > width || watermark.Height > height) {
using (Image image = ZoomOutImage(width, height)) {
return ResizeImageBackgroundToFullPageInternal(width, height, image);
}
}
return ResizeImageBackgroundToFullPageInternal(width, height, watermark);
}
private Image ResizeImageBackgroundToFullPageInternal(int pageWidth, int pageHeight, Image image) {
Image bitmap = new Bitmap(pageWidth, pageHeight);
using Graphics graphics = Graphics.FromImage(bitmap);
graphics.Clear(Color.White);
graphics.DrawImage(image, (pageWidth - image.Width) / 2, (pageHeight - image.Height) / 2);
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.Save();
return bitmap;
}使用 EPPlus 產生浮水印
在支援 System.Drawing.Common 的舊版 EPPlus 中,可透過以下方式設定:
csharp
sheet.HeaderFooter.OddHeader.InsertPicture(watermark, PictureAlignment.Centered);
sheet.BackgroundImage.Image = watermark;WARNING
此作法可能不適用於 EPPlus 6,因該版本已移除對 System.Drawing.Common 的依賴。
使用 NPOI 產生浮水印 (XLSX)
NPOI 並未提供直接設定浮水印的 API,需透過底層操作 VML (Vector Markup Language) 結構來達成。
實作 VML 繪圖物件
由於預設的 XSSFVMLDrawing 僅用於註解 (Comment),需自行定義類別以處理浮水印圖片。
csharp
private class VmlDrawing : POIXMLDocumentPart {
public string PictureRelId { get; set; }
public Image Image { get; set; }
protected override void Commit() {
PackagePart part = GetPackagePart();
Stream @out = part.GetOutputStream();
Write(@out);
@out.Close();
}
private void Write(Stream stream) {
float width = Image.Width * 72 / Image.HorizontalResolution;
float height = Image.Height * 72 / Image.VerticalResolution;
using StreamWriter sw = new(stream);
// 寫入符合 Excel 格式的 VML XML 結構
// 內容包含 v:shape 與 v:imagedata 關聯
}
}設定浮水印流程
- 將圖片轉換為
byte[]並加入 Workbook。 - 建立
VmlDrawing關係並關聯圖片。 - 將
legacyDrawingHF設定至 Worksheet。
csharp
// 設定 Header 浮水印範例
sheet.Header.Center = HeaderFooter.PICTURE_FIELD.sequence;
sheet.GetCTWorksheet().legacyDrawingHF = new CT_LegacyDrawing {
id = sheet.GetRelationId(drawing)
};異動歷程
- 2023-02-24 初版文件建立。
